package com.ruoyi.framework.web.service;


import com.ruoyi.common.constant.LoginConstants;
import com.ruoyi.common.constant.RechargeConstants;
import com.ruoyi.common.constant.VipConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.system.domain.YxRechargeOrders;
import com.ruoyi.system.domain.YxUserVipStatuses;
import com.ruoyi.system.domain.YxVipRechargeOptions;
import com.ruoyi.system.service.*;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import org.apache.http.util.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信业务实现类
 */
@Component
public class WePayService {

    private static final Logger log = LoggerFactory.getLogger(WePayService.class);

    /**
     * 小程序id
     */
    @Value("${weChat.appid}")
    private String wxAppId;

    /**
     * 商户号
     */
    @Value("${wePay.merchantId}")
    private String merchantId;

    /**
     * 商户API私钥路径
     */
    @Value("${wePay.privateKeyPath}")
    private String privateKeyPath;

    /**
     * 商户证书序列号
     */
    @Value("${wePay.merchantSerialNumber}")
    private String merchantSerialNumber;

    /**
     * 商户APIV3密钥
     */
    @Value("${wePay.apiV3Key}")
    private String apiV3Key;

    /**
     * 支付回调地址
     */
    @Value("${wePay.notify_url}")
    private String notify_url;

    private JsapiServiceExtension service;

    private NotificationConfig notificationConfig;

    @Autowired
    private IYxVipRechargeOptionsService yxVipRechargeOptionsService;

    @Autowired
    private IYxVipLevelsService yxVipLevelsService;

    @Autowired
    private IYxRechargeOrdersService yxRechargeOrdersService;

    @Autowired
    private IYxUserVipStatusesService yxUserVipStatusesService;

    @Autowired
    private ISysConfigService sysConfigService;


    @PostConstruct
    public void init() {
        // 初始化商户配置
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(merchantId)
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();
        // 初始化服务
        service =
                new JsapiServiceExtension.Builder()
                        .config(config)
                        .signType("RSA") // 不填默认为RSA
                        .build();

        // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
        // 没有的话，则构造一个
        notificationConfig = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();

    }

    /**
     * 获取请求头签名
     */
    public RequestParam getRequestParam(HttpServletRequest request,String body) {

        // 构造 RequestParam
        RequestParam requestParam =
                new RequestParam.Builder()
                        .signature(request.getHeader("Wechatpay-Signature"))
                        .serialNumber(request.getHeader("Wechatpay-Serial"))
                        .nonce(request.getHeader("Wechatpay-Nonce"))
                        .timestamp(request.getHeader("Wechatpay-TimeStamp"))
                        .signType(request.getHeader("Wechatpay-Signature-Type"))
                        .body(body)
                        .build();
        return requestParam;
    }

    public ResponseEntity.BodyBuilder wxNotify(HttpServletRequest request,String body) {

        // 构造 RequestParam
        RequestParam requestParam = getRequestParam(request,body);

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(notificationConfig);

        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.debug(transaction.toString());
            if(!TextUtils.isEmpty(transaction.getOutTradeNo())
                    && Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())){
                // 处理订单信息
                YxRechargeOrders query = new YxRechargeOrders();
                query.setOrderNum(transaction.getOutTradeNo());
                List<YxRechargeOrders> list = yxRechargeOrdersService.selectYxRechargeOrdersList(query);
                if(list != null && list.size() == 1){

                    YxRechargeOrders yxRechargeOrders = list.get(0);
                    // 确保订单金额 和 我们本地订单金额一致
                    if(transaction.getAmount().getTotal().equals( yxRechargeOrders.getAmount().multiply(BigDecimal.valueOf(100)).intValue())){

                        // 更新订单支付状态
                        payOrder(yxRechargeOrders,transaction.getTradeType().name());
                        return ResponseEntity.status(org.springframework.http.HttpStatus.OK);
                    }else {
                        log.debug("yxRechargeOrders.getOrderNum()  = " + yxRechargeOrders.getOrderNum());
                        log.debug("transaction.getAmount().getTotal()  = " + transaction.getAmount().getTotal());
                        log.debug("yxRechargeOrders.getAmount()  = " + yxRechargeOrders.getAmount().multiply(BigDecimal.valueOf(100)).intValue());
                    }
                }
            }
            // 如果处理失败，应返回 4xx/5xx 的状态码，例如 500 INTERNAL_SERVER_ERROR
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return ResponseEntity.status(org.springframework.http.HttpStatus.UNAUTHORIZED);
        }

        // 处理成功，返回 200 OK 状态码
        return ResponseEntity.status(org.springframework.http.HttpStatus.OK);

    }


    private void payOrder(YxRechargeOrders yxRechargeOrders,String payMethon){

        // todo 这里是否需要异步?
        // 更新订单支付状态
        yxRechargeOrders.setPaidAt(new Date());
        yxRechargeOrders.setPayMethon(payMethon);
        yxRechargeOrders.setOrderStatus(RechargeConstants.ORDER_STATUS_PAYED);
        yxRechargeOrdersService.updateYxRechargeOrders(yxRechargeOrders);

        if(yxRechargeOrders.getOptionId() == VipConstants.RECHARGE_OPTION_UPGRADE_ID){
            // 处理会员升级
            handleVipUpgrade(yxRechargeOrders);
        }else {
            // 查询充值详情
            YxVipRechargeOptions options = yxVipRechargeOptionsService.selectYxVipRechargeOptionsById(yxRechargeOrders.getOptionId());

            handleRecharger(yxRechargeOrders, options);
        }


    }

    /**
     * 处理会员升级 直接将会员等级 升级到svip
     * @param yxRechargeOrders
     */
    private void handleVipUpgrade(YxRechargeOrders yxRechargeOrders) {
        // 更新用户会员信息 处理充值信息
        YxUserVipStatuses query = new YxUserVipStatuses();
        query.setUserId(yxRechargeOrders.getUserId());
        List<YxUserVipStatuses> list = yxUserVipStatusesService.selectYxUserVipStatusesList(query);

        Map<Long,YxUserVipStatuses> userVipStatusesMap = new HashMap();
        for(YxUserVipStatuses userVipStatuses1 : list){
            userVipStatusesMap.put(userVipStatuses1.getVipLevelId(),userVipStatuses1);
        }

        // 直接将svip时间 更新为 vip时间
        if(userVipStatusesMap.containsKey(VipConstants.VIP_LEVEL_VIP)){
            if(userVipStatusesMap.containsKey(VipConstants.VIP_LEVEL_SVIP)){
                YxUserVipStatuses yxUserVipStatuses = userVipStatusesMap.get(VipConstants.VIP_LEVEL_SVIP);
                if(yxUserVipStatuses.getExpirationDate().after(DateUtils.getNowDate())){
                    // 直接将svip时间 更新为 vip时间
                    yxUserVipStatuses.setExpirationDate(userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP).getExpirationDate());
                    yxUserVipStatusesService.updateYxUserVipStatuses(yxUserVipStatuses);
                }
            }else{
                // 没有svip 则直接更新当前会员等级
                YxUserVipStatuses yxUserVipStatuses = userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP);
                if(yxUserVipStatuses.getExpirationDate().after(DateUtils.getNowDate())){
                    // 升级vip会员
                    yxUserVipStatuses.setVipLevelId(VipConstants.VIP_LEVEL_SVIP);
                    yxUserVipStatusesService.updateYxUserVipStatuses(yxUserVipStatuses);
                }
            }
        }

    }

    /**
     * 1、vip 和 svip 独立充值
     * 2、svip充值立即生效，之前的会员时间往后移动
     * 3、vip默认后置，在之前的会员时间往后追加
     * @param yxRechargeOrders
     * @param options
     */
    private void handleRecharger(YxRechargeOrders yxRechargeOrders, YxVipRechargeOptions options) {
        // 更新用户会员信息 处理充值信息
        YxUserVipStatuses query = new YxUserVipStatuses();
        query.setUserId(yxRechargeOrders.getUserId());
        List<YxUserVipStatuses> list = yxUserVipStatusesService.selectYxUserVipStatusesList(query);

        if(list == null || list.size() == 0){
            // 暂无等级 直接进行会员时间插入
            YxUserVipStatuses yxUserVipStatuses = new YxUserVipStatuses();
            yxUserVipStatuses.setVipLevelId(options.getVipLevel());
            yxUserVipStatuses.setUserId(yxRechargeOrders.getUserId());
            yxUserVipStatuses.setExpirationDate(DateUtils.getTimestampByMonth(options.getDuration().intValue()));
            yxUserVipStatusesService.insertYxUserVipStatuses(yxUserVipStatuses);
        }else {
            // 已有等级
            Map<Long,YxUserVipStatuses> userVipStatusesMap = new HashMap();
            for(YxUserVipStatuses userVipStatuses : list){
                userVipStatusesMap.put(userVipStatuses.getVipLevelId(),userVipStatuses);
            }
            // 区分充值类型
            if(options.getVipLevel().equals(VipConstants.VIP_LEVEL_SVIP)){
                // svip充值
                handleSVipRecharger(yxRechargeOrders,options,userVipStatusesMap);

            }else if(options.getVipLevel().equals(VipConstants.VIP_LEVEL_VIP)){
                // vip充值
                handleVipRecharger(yxRechargeOrders,options,userVipStatusesMap);
            }
        }
    }

    /**
     * 处理vip充值 时间自动往后延长
     * @param yxRechargeOrders
     * @param options
     * @param userVipStatusesMap
     */
    private void handleVipRecharger(YxRechargeOrders yxRechargeOrders,
                                    YxVipRechargeOptions options,
                                    Map<Long,YxUserVipStatuses> userVipStatusesMap){
        // vip充值
        // 判断充值的时间
        YxUserVipStatuses yxUserVipStatuses = userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP);

        //处理vip延长
        YxUserVipStatuses yxUserSVip = userVipStatusesMap.get(VipConstants.VIP_LEVEL_SVIP);

        //如果 已有SVIP会员 且未过期 则进行后延
        if(yxUserSVip != null && yxUserSVip.getExpirationDate().after(DateUtils.getNowDate())){
            if(yxUserVipStatuses == null){
                // 暂无等级 直接进行会员时间插入
                yxUserVipStatuses = new YxUserVipStatuses();
                yxUserVipStatuses.setVipLevelId(options.getVipLevel());
                yxUserVipStatuses.setUserId(yxRechargeOrders.getUserId());
                yxUserVipStatuses.setExpirationDate(DateUtils.getTimestampByMonth(yxUserSVip.getExpirationDate(),options.getDuration().intValue()));
                yxUserVipStatusesService.insertYxUserVipStatuses(yxUserVipStatuses);
            }else if(yxUserVipStatuses.getExpirationDate().after(DateUtils.getNowDate())){
                // 有等级 直接进行会员时间更新
                yxUserVipStatuses.setExpirationDate(DateUtils.getTimestampByMonth(yxUserSVip.getExpirationDate(),options.getDuration().intValue()));
                yxUserVipStatusesService.updateYxUserVipStatuses(yxUserVipStatuses);
            }

        }else {
            if(yxUserVipStatuses == null){
                // 暂无等级 直接进行会员时间更新
                yxUserVipStatuses = new YxUserVipStatuses();
                yxUserVipStatuses.setVipLevelId(options.getVipLevel());
                yxUserVipStatuses.setUserId(yxRechargeOrders.getUserId());
                yxUserVipStatuses.setExpirationDate(DateUtils.getTimestampByMonth(options.getDuration().intValue()));
                yxUserVipStatusesService.insertYxUserVipStatuses(yxUserVipStatuses);
            }else if(yxUserVipStatuses.getExpirationDate().after(DateUtils.getNowDate())){
                // 会员时间未过期 则基于当前有效期往后顺延
                yxUserVipStatuses.setExpirationDate(DateUtils.getTimestampByMonth(yxUserVipStatuses.getExpirationDate(),options.getDuration().intValue()));
                yxUserVipStatusesService.updateYxUserVipStatuses(yxUserVipStatuses);

            }else {
                // 会员时间已过期
                yxUserVipStatuses.setExpirationDate(DateUtils.getTimestampByMonth(options.getDuration().intValue()));
                yxUserVipStatusesService.updateYxUserVipStatuses(yxUserVipStatuses);
            }
        }


    }

    /**
     * 处理svip 充值
     * @param yxRechargeOrders
     * @param options
     * @param userVipStatusesMap
     */
    private void handleSVipRecharger(YxRechargeOrders yxRechargeOrders,
                                     YxVipRechargeOptions options,
                                     Map<Long,YxUserVipStatuses> userVipStatusesMap){
        // svip充值
        // 判断充值的时间
        YxUserVipStatuses yxUserSVip = userVipStatusesMap.get(VipConstants.VIP_LEVEL_SVIP);

        if(yxUserSVip == null){
            // 暂无等级 直接进行sip会员时间更新
            yxUserSVip = new YxUserVipStatuses();
            yxUserSVip.setVipLevelId(options.getVipLevel());
            yxUserSVip.setUserId(yxRechargeOrders.getUserId());
            yxUserSVip.setExpirationDate(DateUtils.getTimestampByMonth(options.getDuration().intValue()));
            yxUserVipStatusesService.insertYxUserVipStatuses(yxUserSVip);

        }else if(yxUserSVip.getExpirationDate().after(DateUtils.getNowDate())){
            // SVIP会员时间未过期 则基于当前有效期往后顺延
            yxUserSVip.setExpirationDate(DateUtils.getTimestampByMonth(yxUserSVip.getExpirationDate(),options.getDuration().intValue()));
            yxUserVipStatusesService.updateYxUserVipStatuses(yxUserSVip);

        }else {
            // SVIP会员时间已过期
            yxUserSVip.setExpirationDate(DateUtils.getTimestampByMonth(options.getDuration().intValue()));
            yxUserVipStatusesService.updateYxUserVipStatuses(yxUserSVip);
        }

        //处理vip延长
        YxUserVipStatuses yxUserVip = userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP);
        //如果 已有会员 且未过期 则进行后延
        if(yxUserVip != null && yxUserVip.getExpirationDate().after(DateUtils.getNowDate())){
            //vip会员未过期 则进行后延
            yxUserVip.setExpirationDate(DateUtils.getTimestampByMonth(yxUserVip.getExpirationDate(),options.getDuration().intValue()));
            yxUserVipStatusesService.updateYxUserVipStatuses(yxUserVip);
        }
    }


    public R prepay(Long optionsId) {

        if (optionsId == null || optionsId == 0) {
            return R.fail("optionsId 不可为空");
        }
        YxVipRechargeOptions options = null;
        // vip升级处理
        if(optionsId == VipConstants.RECHARGE_OPTION_UPGRADE_ID){
            // 增加会员升级判断
            SysUser user = SecurityUtils.getLoginUser().getUser();
            YxUserVipStatuses query = new YxUserVipStatuses();
            query.setUserId(user.getUserId());

            query.setExpirationDate(DateUtils.getNowDate());
            List<YxUserVipStatuses> userVipStatusesList = yxUserVipStatusesService.selectYxUserVipStatusesList(query);

            if(userVipStatusesList == null || userVipStatusesList.size() == 0) {
                return R.fail("无vip可升级");
            }

            Map<Long,YxUserVipStatuses> userVipStatusesMap = new HashMap();
            for(YxUserVipStatuses userVipStatuses1 : userVipStatusesList){
                userVipStatusesMap.put(userVipStatuses1.getVipLevelId(),userVipStatuses1);
            }



            // 计算剩余天数
            int day = 0;
            if(userVipStatusesMap.containsKey(VipConstants.VIP_LEVEL_SVIP)){
                if(userVipStatusesMap.get(VipConstants.VIP_LEVEL_SVIP).getExpirationDate().equals(userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP).getExpirationDate())){
                    return R.fail("无vip可升级");
                }
                day = DateUtils.differentDaysByMillisecond(userVipStatusesMap.get(VipConstants.VIP_LEVEL_SVIP).getExpirationDate(),userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP).getExpirationDate()) + 1;
            }else {
                day = DateUtils.differentDaysByMillisecond(DateUtils.getNowDate(),userVipStatusesMap.get(VipConstants.VIP_LEVEL_VIP).getExpirationDate()) + 1;
            }
            options = new YxVipRechargeOptions();
            String vipUpgradePrivce = sysConfigService.selectConfigByKey("sys.pay.vipUpgradePrivce");
            if(TextUtils.isEmpty(vipUpgradePrivce)){
                vipUpgradePrivce = VipConstants.VIP_UPGRADE_PRIVCE_DEFAULT;
            }
            options.setPrice(new BigDecimal(vipUpgradePrivce).multiply(BigDecimal.valueOf(day)));
            options.setVipName("升级SVIP");
        }else {
            options = yxVipRechargeOptionsService.selectYxVipRechargeOptionsById(optionsId);
        }


        if (options == null || options.getPrice() == null || options.getPrice().compareTo(BigDecimal.ZERO) == 0) {
            return R.fail("options 异常");
        }

        //获取当前登录用户
        SysUser user = SecurityUtils.getLoginUser().getUser();
        if (user.getAccountType() != LoginConstants.USER_TYPE_WX) {
            return R.fail("用户信息 异常");
        }

        // 1、生成本地支付订单
        String out_trade_no = UUID.randomUUID().toString().replace("-", "");
        Integer total = options.getPrice().multiply(BigDecimal.valueOf(100)).intValue();
        String description = options.getVipName();

        YxRechargeOrders yxRechargeOrders = new YxRechargeOrders();
        yxRechargeOrders.setAmount(options.getPrice());
        yxRechargeOrders.setCreatedAt(new Date());
        yxRechargeOrders.setOptionId(optionsId);
        yxRechargeOrders.setOptionType(RechargeConstants.VIP_RECHARGE);
        yxRechargeOrders.setOrderNum(out_trade_no);
        yxRechargeOrders.setUserId(user.getUserId());
        yxRechargeOrders.setOrderStatus(RechargeConstants.ORDER_STATUS_CREATED);
        yxRechargeOrders.setRemark(description);
        yxRechargeOrdersService.insertYxRechargeOrders(yxRechargeOrders);


        // 2、生成微信预支付订单
        String openId = user.getUserName().replace(LoginConstants.WX_PREX, "");
        try {
            PrepayWithRequestPaymentResponse response = prepayWithRequestPayment(out_trade_no, total, description, openId);
            log.debug(response.toString());
            return R.ok(response);
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            e.printStackTrace();
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            e.printStackTrace();
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            e.printStackTrace();
        }

        return R.fail("获取失败");

    }

    /**
     * 关闭订单
     */
    public void closeOrder() {

        CloseOrderRequest request = new CloseOrderRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        service.closeOrder(request);
    }

    /**
     * JSAPI支付下单，并返回JSAPI调起支付数据
     */
    private PrepayWithRequestPaymentResponse prepayWithRequestPayment(String out_trade_no, Integer total, String description, String openId) {
        PrepayRequest request = new PrepayRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        request.setMchid(merchantId);
        request.setOutTradeNo(out_trade_no);
        request.setAppid(wxAppId);
        request.setDescription(description);
        request.setNotifyUrl(notify_url);
        Amount amount = new Amount();

        amount.setCurrency("CNY");
        amount.setTotal(total);
        request.setAmount(amount);

        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);

        // 调用接口
        return service.prepayWithRequestPayment(request);
    }

    /**
     * 微信支付订单号查询订单
     */
    public Transaction queryOrderById() {

        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        return service.queryOrderById(request);
    }

    /**
     * 商户订单号查询订单
     */
    public Transaction queryOrderByOutTradeNo() {

        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        return service.queryOrderByOutTradeNo(request);
    }

}
